package service;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.StringTokenizer;

import javax.mail.MessagingException;
import javax.mail.internet.AddressException;

import model.Entrada;
import model.Usuario;
import util.VerificadorExcecoes;
import excecao.SistemaException;

public class GerenciadorSistema {

	private HashMap<String, Usuario> conjuntoDeUsuarios;

	private GerenciadorSessao gerenciadorSessao;

	private GerenciadorGrupos gerenciadorGrupos;
	
	private GerenciadorEnvioLembretes gerenciadorEnvioLembretes;

	/**
	 * Construtor do sistema Lembrete
	 * 
	 */
	public GerenciadorSistema() {

		conjuntoDeUsuarios = new HashMap<String, Usuario>();
		gerenciadorSessao = new GerenciadorSessao();
		gerenciadorGrupos = new GerenciadorGrupos();
		gerenciadorEnvioLembretes = new GerenciadorEnvioLembretes();
	}

	/**
	 * Cria um novo usuario adicionando ao conjunto de usuarios
	 * 
	 * @param nome
	 *            Nome do usuario
	 * @param sobrenome
	 *            Sobrenome do usuario
	 * @param email
	 *            Email do usuario
	 * @param senha
	 *            Senha do usuario
	 * 
	 * @return Retorna a Id unica do usuario
	 * @throws SistemaException
	 */
	public void criarUsuario(String nome, String sobrenome, String email,
			String senha) throws SistemaException {

		if (emailJaCadastrado(email)) {
			throw new SistemaException("Email jah cadastrado no sistema.");

		}
		Usuario usuario = new Usuario(nome, sobrenome, email, senha);
		conjuntoDeUsuarios.put(usuario.getEmail(), usuario);

	}

	/**
	 * Retorna o Usuario correspondente a id unica passada como parametro
	 * 
	 * @param id
	 *            Id unica do usuario passada como parametro
	 * @return Usuario correspondente a id unica passada como parametro
	 * @throws SistemaException
	 */
	public Usuario getUsuario(String email) throws SistemaException {
		VerificadorExcecoes.verificaStringVazia(email,
				"Email do usuario nao pode ser nulo.");
		if (conjuntoDeUsuarios.containsKey(email)) {
			Usuario usuario = conjuntoDeUsuarios.get(email);
			return usuario;
		}
		throw new SistemaException("Usuario nao existe.");
	}

	/**
	 * 
	 * Inicia uma sessao de um usuario no sitema.
	 * 
	 * @param email
	 *            Email do usuario a ser aberto a sessao.
	 * @param senha
	 *            Senha do usuario a ser aberto Sessao.
	 * @return Id(identifica��o) unica para referenciar a essa sessao.
	 * @throws SistemaException
	 */
	public String abrirSessao(String email, String senha)
			throws SistemaException {
		if (conjuntoDeUsuarios.containsKey(email)) {
			Usuario usuario = conjuntoDeUsuarios.get(email);
			if (usuario.getSenha().equals(senha)) {
				return gerenciadorSessao.abrirSessao(email);
			}
		}
		throw new SistemaException("Email ou senha invalidos.");

	}

	/**
	 * 
	 * Altera um atributo do ususario, n�o pode alterar o e-mail.
	 * 
	 * @param idSessao
	 *            Identificacao da sessao aberta
	 * @param atributo
	 *            Atributo que deseja ser alterado (Nome,sobrenome,senha)
	 * @param valor
	 *            O novo valor que devera substituir o pre-existente
	 * 
	 * @throws SistemaException
	 */
	public void alteraUsuario(String idSessao, String atributo, String valor)
			throws SistemaException {

		Usuario usuario = getUsuarioPorSessao(idSessao);
		usuario.alteraAtributo(atributo, valor);

	}

	/**
	 * Retorna um usuario referente a sua sess�o.
	 * 
	 * @param idSessao
	 *            Identificacao da sessao.
	 * @return Usuario referente a sessao passada como parametro.
	 * @throws SistemaException
	 */
	private Usuario getUsuarioPorSessao(String idSessao)
			throws SistemaException {

		if (gerenciadorSessao.sessaoValida(idSessao)) {
			String emailUsuario = gerenciadorSessao.getEmailUsuario(idSessao);
			Usuario usuario = conjuntoDeUsuarios.get(emailUsuario);
			return usuario;

		} else {
			throw new SistemaException("Usuario nao existe.");
		}
	}

	/**
	 * 
	 * @param email
	 *            Email que serah verificado se jah eh cadastrado no sitema
	 * 
	 * @return True caso o e-mail ja esteja cadastrado no sistema, False caso
	 *         contrario
	 */
	private boolean emailJaCadastrado(String email) {
		return conjuntoDeUsuarios.containsKey(email);
	}

	/**
	 * Cria uma entrada de um usuario.
	 * 
	 * @param idSessao
	 *            Identificacao da sessao.
	 * @param nomeContato
	 *            Nome da entrada.
	 * @param telefone
	 *            Telefone da entrada
	 * @param dataAniversario
	 *            Data do aniversario da entrada
	 * @throws SistemaException
	 */
	public String criarEntrada(String idSessao, String nomeContato,
			String telefone, String dataAniversario) throws SistemaException {

		Usuario usuario = getUsuarioPorSessao(idSessao);
		return usuario.criarEntrada(nomeContato, telefone, dataAniversario);

	}

	/**
	 * 
	 * @param idSessao
	 * @param idEntrada
	 * @param atributo
	 * @return
	 * @throws SistemaException
	 */
	public String getAtributoEntrada(String idSessao, String idEntrada,
			String atributo) throws SistemaException {

		Usuario usuario = getUsuarioPorSessao(idSessao);
		return usuario.getAtributoEntrada(idEntrada, atributo);
	}

	/**
	 * 
	 * @param idSessao
	 * @return
	 * @throws SistemaException
	 */
	public String getEntradasUsuario(String idSessao) throws SistemaException {

		Usuario usuario = getUsuarioPorSessao(idSessao);
		return usuario.getEntradas();

	}

	/**
	 * 
	 * @param idSessao
	 * @param idEntrada
	 * @param atributo
	 * @param valor
	 * @throws SistemaException
	 */
	public void alteraEntrada(String idSessao, String idEntrada,
			String atributo, String valor) throws SistemaException {

		Usuario usuario = getUsuarioPorSessao(idSessao);
		usuario.alteraEntrada(idEntrada, atributo, valor);

	}

	public void alteraEntradaGrupo(String idSessao, String idGrupo,
			String idEntrada, String atributo, String valor) throws SistemaException {
		
		String emailUsuario = getUsuarioPorSessao(idSessao).getEmail();
		gerenciadorGrupos.alteraEntrada(emailUsuario,idGrupo,idEntrada,atributo,valor);
		
	}

	/**
	 * 
	 * @param idSessao
	 * @param nomeGrupo
	 * @param permissao
	 * @return
	 * @throws SistemaException
	 */
	public String criarGrupo(String idSessao, String nomeGrupo, String permissao)
			throws SistemaException {

		Usuario usuario = getUsuarioPorSessao(idSessao);
		String emailUsuario = usuario.getEmail();
		if (usuario.jaTemGrupo(nomeGrupo)) {
			throw new SistemaException("Usuario ja tem um grupo com esse nome.");
		} else {
			String idGrupo = gerenciadorGrupos.criarGrupo(emailUsuario,
					nomeGrupo, permissao);
			usuario.addGrupo(idGrupo);
			return idGrupo;
		}
	}

	/**
	 * Retorna um atributo do grupo
	 * 
	 * @param idGrupo
	 *            Identificacao do grupo.
	 * @param atributo
	 *            Atributo que se deseja.
	 * @return Valor do atributo do grupo
	 * @throws SistemaException
	 */
	public String getAtributoGrupo(String idGrupo, String atributo)
			throws SistemaException {

		return gerenciadorGrupos.getAtributoGrupo(idGrupo, atributo);

	}

	/**
	 * Retorna email do dono(criador) do grupo.
	 * 
	 * @param idGrupo
	 *            Identificacao do grupo.
	 * @return Email do dono(criador) do grupo.
	 * @throws SistemaException
	 */
	public String getDonoGrupo(String idGrupo) throws SistemaException {

		return gerenciadorGrupos.getDonoGrupo(idGrupo);
	}

	public String getMembrosGrupo(String idGrupo) throws SistemaException {

		return gerenciadorGrupos.getMembrosGrupo(idGrupo);
	}

	public void alteraGrupo(String idGrupo, String atributo, String valor)
			throws SistemaException {
		gerenciadorGrupos.alteraGrupo(idGrupo, atributo, valor);
	}

	/**
	 * Adiciona uma Entrada ao grupo
	 * 
	 * @param idSessao
	 *            Identificacao da sessao
	 * @param idGrupo
	 *            Identificacao do grupo
	 * @param idEntrada
	 *            Identificacao da entrada
	 * @throws SistemaException
	 */
	public void adicionaEntradaGrupo(String idSessao, String idGrupo,
			String idEntrada) throws SistemaException {

		Usuario usuario = getUsuarioPorSessao(idSessao);
		Entrada entrada = usuario.getEntrada(idEntrada);
		gerenciadorGrupos.addEntrada(idGrupo, entrada);
		// Esse metodo serve pra guardar quais grupos uma entrada participa,
		// para quando for excluir uma entrada de uma agenda, poder tambem saber
		// de quais grupos tem que exclui-la.
		entrada.addGrupoQueParticipa(idGrupo);
	}

	/**
	 * Retorna as Entradas pertencente ao grupo.
	 * 
	 * @param idGrupo
	 *            Identificacao do grupo
	 * @return Entradas pertencente ao grupo
	 * @throws SistemaException
	 */
	public String getEntradasGrupo(String idGrupo) throws SistemaException {

		return gerenciadorGrupos.getEntradasGrupo(idGrupo);
	}

	/**
	 * Exclui entrada da agenda. Excluindo a entrada da agenda, tambem sera
	 * excluida do grupo.
	 * 
	 * @param idSessao
	 *            Identificacao da sessao.
	 * @param idEntrada
	 *            Identificacao da Entrada.
	 * @throws SistemaException
	 */
	public void excluirEntradaAgenda(String idSessao, String idEntrada)
			throws SistemaException {

		Usuario usuario = getUsuarioPorSessao(idSessao);
		Entrada entrada = usuario.getEntrada(idEntrada);
		gerenciadorGrupos.excluirEntradaGrupos(entrada);
		usuario.excluirEntradaAgenda(idEntrada);

	}

	/**
	 * Exclui uma Entrada de um Grupo, excluindo tambem da agenda do usuario, e
	 * dos outros grupos que ela pertence.
	 * 
	 * @param idSessao
	 * @param idGrupo
	 * @param idEntrada
	 * @throws SistemaException
	 */
	public void excluirEntradaGrupo(String idSessao, String idGrupo,
			String idEntrada) throws SistemaException {
		excluirEntradaAgenda(idSessao, idEntrada);

	}

	public String getAtributoEntradaGrupo(String idGrupo, String idEntrada,
			String atributo) throws SistemaException {

		return gerenciadorGrupos.getAtributoEntradaGrupo(idGrupo, idEntrada,
				atributo);
	}

	/**
	 * 
	 * @param idSessao
	 * @param idGrupo
	 * @param emailConvidado
	 * @throws SistemaException
	 */
	public void compartilharGrupo(String idSessao, String idGrupo,
			String emailConvidado) throws SistemaException {

		Usuario usuarioDono = getUsuarioPorSessao(idSessao);
		Usuario usuarioConvidado = getUsuario(emailConvidado);
		gerenciadorGrupos.fazerConvite(usuarioConvidado, idGrupo);

	}

	/**
	 * 
	 * 
	 * @param idSessao
	 * @param idGrupo
	 * @throws SistemaException
	 */
	public void participarGrupo(String idSessao, String idGrupo)
			throws SistemaException {

		Usuario usuarioConvidado = getUsuarioPorSessao(idSessao);
		usuarioConvidado.participarGrupo(idGrupo);
		gerenciadorGrupos.addUsuarioGrupo(idGrupo, usuarioConvidado.getEmail());

	}

	
	/**
	 * Usuario deixa de participar de um grupo que compartilhava.
	 * 
	 * @param idSessao Identificacao da sessao do usuario.
	 * @param idGrupo Identificacao do grupo.
	 * @throws SistemaException Excessao do sistema.
	 */
	public void deixarGrupo(String idSessao, String idGrupo)
			throws SistemaException {
		
		Usuario usuario = getUsuarioPorSessao(idSessao);
		usuario.deixarGrupo(idGrupo);
		gerenciadorGrupos.removeUsuarioGrupo(idGrupo,usuario.getEmail());
		
		
	}

	/**
	 * 
	 * 
	 * @param idSessao
	 * @return
	 * @throws SistemaException
	 */
	public String getGruposUsuario(String idSessao) throws SistemaException {
		
		Usuario usuario = getUsuarioPorSessao(idSessao);
		return usuario.getGrupos();
		
	}

	/**
	 * Envia lembretes aos usuarios.
	 * 
	 * @param data Data do dia corrente.
	 * @throws SistemaException Excessao do sistema.
	 * @throws AddressException E-mail invalido, passado como parametro.
	 * @throws MessagingException Erro na msg do e-mail.
	 */
	public void enviarLembrentes(String data) throws SistemaException, AddressException, MessagingException {
		
		Set conjuntoChavesUsuarios = this.conjuntoDeUsuarios.keySet();
		Iterator it = conjuntoChavesUsuarios.iterator(); 
		while (it.hasNext()){
			String email = (String)it.next();
			Usuario usuario = getUsuario(email);
			gerenciadorEnvioLembretes.enviarLembreteUsuario(data,usuario);
		}
		
		
		
	}

	/**
	 * Exclui um grupo de um usuario.
	 * 
	 * @param idSessao Identificacao da sessao.
	 * @param idGrupo Identificacao do grupo.
	 * @throws SistemaException Excessao do sistema.
	 */
	public void excluirGrupo(String idSessao, String idGrupo) throws SistemaException {
		
		
		Usuario usuario = getUsuarioPorSessao(idSessao);
		usuario.removeGrupo(idGrupo);
		gerenciadorGrupos.excluirGrupo(idGrupo,usuario.getEmail());
		
	}

	public void excluirUsuario(String idSessao) throws SistemaException {
		
		//Usuario exclui entradas de sua agenda.
		Usuario usuario = getUsuarioPorSessao(idSessao);
		usuario.excluirEntradas();
		
		//Usuario exclui os grupos que eh dono, e deixa de participar dos que compartilhava.
		String conjuntoGrupos = usuario.getGrupos();
		conjuntoGrupos = conjuntoGrupos.replace("[", "").replace("]", "")
				.replace(",", "");
		StringTokenizer st = new StringTokenizer(conjuntoGrupos);
		while (st.hasMoreTokens()) {
			String idGrupo = st.nextToken();
			if(gerenciadorGrupos.getDonoGrupo(idGrupo).equals(usuario.getEmail())){
				excluirGrupo(idSessao, idGrupo);
			}else{
				usuario.deixarGrupo(idGrupo);
			}
			
			
		}
		
		conjuntoDeUsuarios.remove(usuario.getEmail());
		
		
	}
	


}
